home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Tech Arsenal 1
/
Tech Arsenal (Arsenal Computer).ISO
/
tek-05
/
lanutsrc.zip
/
INTERCOM.ASM
< prev
next >
Wrap
Assembly Source File
|
1991-03-13
|
25KB
|
912 lines
INCLUDE \ASM\INCLUDE\BIOS.INC
INCLUDE DEFS.INC
SOUND EQU 61h
TIMER EQU 42h
SPK_ON EQU 3
VECTOR1 EQU 09h ; BIOS keyboard handler
IROW EQU 5 ; Starting row,col of window
ICOL EQU 10
IHEIGHT EQU 5 ; Size of window
IWIDTH EQU 63
; status constants
MAX_DEST EQU 16 ; length of destination field
MAX_MSG EQU IWIDTH - 2 ; length of message field
DEST EQU 0 ; which field is active?
MSG EQU 0FFFFh
; key codes
UP EQU 4800h ; up arrow
DN EQU 5000h ; down arrow
LEFT EQU 4B00h ; left arrow
RIGHT EQU 4D00h ; right arrow
RETURN EQU 1C0Dh ; return key
BACK EQU 0E08h ; backspace key
INSERT EQU 5200h
DELETE EQU 5300h
HOME EQU 4700h
ENDKEY EQU 4F00h ; End key
ESCAPE EQU 011Bh ; Escape key
msv SEGMENT
ASSUME CS:msv
ASSUME DS:msv
ASSUME ES:msv
ASSUME SS:NOTHING
ORG 0
SEG_ORG EQU $
ORG 100h
main PROC FAR
start: jmp init ; Do initialization section
;*******************************************************************
; LANOS VECTOR TSR portion of Message handler --
; The LANOS portion simply increments a message received flag and
; exits, leaving the actual processing of the message to an
; interrupt.
;*******************************************************************
MSV_entry: jmp first ; skip data area
msg_flag dw 0
old_msr dd ?
;************************************************************************
; Start of actual LANOS message handler. Note that this handler DOES NOT
; call the previously chained vector -- while the intercom is popped up,
; no other message handling services will be activated.
;************************************************************************
first:
mov cs:msg_flag,1 ; set the message received flag
iret ; return to caller
; ********************************************************************
; Interrupt 09 - Keyboard interrupt handler
; Thanks to Joe and Sam for demonstrating the right way to do it!
; ********************************************************************
INT09_entry:
push ax
cmp cs:busy,0 ; are we already popped up?
jnz Done_09 ; if so, continue
in al,60h ; Read the character from the kbd
CMP AL,CS:hotkey ; is it for us?
JNZ Done_09 ; if not, continue
; Check the keyboard status bits to see if it's for us
PUSH DS
MOV AX,40H ; POINT TO THE BIOS
MOV DS,AX
MOV AL,DS:17H ; Get kbd status byte
POP DS
AND AL,CS:kbd_bits ; ARE THESE BITS ON?
CMP AL,CS:kbd_bits ; WELL?
JNZ Done_09 ; NOPE NOT AT THIS TIME
; reset the keyboard
IN AL,61H ; GET KBD CONTROL CODE
MOV AH,AL
OR AL,80H ; SET RESET BIT
OUT 61H,AL ; SEND RESET BACK
MOV AL,AH ; GET BACK THE CONTROL CODE
OUT 61H,AL ; RESET DONE
MOV AL,20H ; RESET HIGHEST IUS
OUT 20H,AL
inc cs:request ; issue a pop up request
pop ax
IRET ; back to regularly scheduled program
Done_09:
POP AX
JMP CS:old_vector1 ; CONTINUE ON AS IF WE AREN'T HERE
;*****************************************************************************
; Interrupt E0 handler --- this is apparently something that LANtastic uses
; internally to indicate when DOS is safe.
;*****************************************************************************
INT_E0 DD 0
DOSFREE:
PUSHF
CALL CS:old_vector2 ; give everyone else a chance...
cmp cs:request,0 ; have our services been requested?
jz Done_E0 ; If not, skip this stuff
CMP CS:busy,0 ; Are we already busy?
JNZ Done_E0 ; If so, skip this stuff
; Activate the pop up routine
dec cs:request ; cancel previous request
inc cs:busy ; set busy flag
call pop_up
dec cs:busy
Done_E0:
IRET
;***********************************************************************
; Pop-up intercom
; When we're popped up, an Esc keypress will take us back to normal
; processing
;***********************************************************************
pop_up PROC near
jmp get_msg ; bypass data area
; housekeeping stuff and screen data area.
db '■S' ; ID string
color db 70h ; Default window color
hotkey db 23 ; Activation key (I for now)
kbd_bits db 8 ; Alt
busy db 0 ; TSR status flag
request db 0 ; TSR activation request flag
screen_data dw (IHEIGHT * IWIDTH) DUP (?)
from db 'From: ',0 ; Onscreen prompts and instructions
toquit db 'Press Esc to exit',0
old_vector1 dd ? ; old int vector
old_vector2 dd ?
old_cursor dw ? ; old cursor position
msg_state db ? ; old message processing flag
; program status variables
state db ? ; current field id
ins_flag db 0 ; insert key state flag
d_index field_desc <0,0,16,OFFSET buffer.MB_machine>
m_index field_desc <0,0,61,OFFSET buffer.MB_text>
c_field dw 0 ; pointer to current field descriptor
done db 0 ; completion flag
buffer message_buffer <> ; message buffer for internal use
inbuffer message_buffer <> ; buffer for incoming messages
prompt1 db "To:─────",0 ; prompt text messages
prompt2 db "Message:",0
; stack information
old_stk_seg dw ? ; Calling program's stack info
old_b_ptr dw ?
old_stk_ptr dw ?
new_stk_seg dw ?
new_stk_ptr dw OFFSET TOS
dw 80 dup (?) ; local stack data area
TOS equ $
get_msg:
@NewStack ; replace stack and save regs
push cs ; set all seg regs to current CS
push cs
pop es
pop ds
; save calling cursor information
IC1:
mov ah,3 ; read cursor info
xor bh,bh ; for page 0
int 10h
mov cs:old_cursor,dx ; save cursor info
;
; Replace the LANOS message service with our message handler
;
; Save the message processing flag
mov ax,5F9Ah
int 21h
mov cs:msg_state,dl
; Get addresss of old LANOS message handler
mov ax,5FE2h ; Get old vector address
int 21h
mov word ptr old_msr,bx ; Save the location of the old vector
mov word ptr old_msr+2,es
; Set MSR vector to our routine
mov ax,cs ; segment address of our MSR
mov es,ax
mov bx,OFFSET MSV_entry ; offset of MSR
mov ax,5FE3h ; set MSR vector function
int 21H
;
; Tell message handler not to beep
;
mov ax,5F9Bh ; set message processing flag
mov dl,2 ; deliver, but no beep
int 21h
;
; save underlying screen and draw intercom window
;
mov di, OFFSET screen_data ; point to save area
mov dh,IROW ; starting screen row
mov cx,IHEIGHT
doRow: ; For each row...
push cx ; save number of rows
mov dl,ICOL ; starting column on screen
call CURSOR ; move cursor to row start
mov cx,IWIDTH ; number of columns
DoCol: ; For each character
push cx ; save char count
@GetChAtr 0 ; Get the char and attribute
stosw ; move it to the save area
inc dl ; move cursor
call CURSOR
pop cx ; retrieve char count
loop SHORT DoCol ; go back for next char
inc dh ; point to next screen row
pop cx ; retrieve row count
loop SHORT DoRow
; Draw intercom window
DRAW_LINE '┌','─','╖',IROW
MOVE_CURSOR IROW,ICOL+1
mov di, OFFSET from
call PRT_STR
DRAW_LINE '│',' ','║',IROW+1
DRAW_LINE '├','─','╢',IROW+2
DRAW_LINE '│',' ','║',IROW+3
DRAW_LINE '╘','═','╝',IROW+4
MOVE_CURSOR IROW+4,ICOL+45
mov di, OFFSET toquit
call PRT_STR
;
; Initialize variables
;
push cs ; set all seg regs to current CS
push cs
pop es
pop ds
mov done,0
mov c_field,OFFSET d_index ;destination field
;
; Get user input
;
mov dh,IROW + 2 ; display field name prompt
mov dl,ICOL + 1
call CURSOR
mov di,OFFSET prompt1
call PRT_STR
mov dh,IROW + 3 ; display destination prompt
mov dl,ICOL + 1
call CURSOR
mov di,OFFSET buffer.MB_machine
call PRT_STR
mov cs:state,DEST ; set field to destination
mov dh,IROW + 3 ; move cursor to start of field
mov dl,ICOL + 1
add dl,d_index.FD_index ; move to last cursor pos
mov msg_flag,1 ; set up to display last message
Get_input:
cmp msg_flag,1 ; see if a message is ready
jne GI1
call SHOW_MESSAGE ; if we've got one, show it.
GI1:
call CURSOR ; move cursor to appropriate place
mov ax,0100h ; get keyboard status fn.
int 16h
jz Get_input ; if not, go back
xor ax,ax ; Read keyboard char fn.
int 16h
;
; Process each keystroke
;
cmp ax,UP
jne N1
jmp Toggle
N1: cmp ax,DN
jne N2
Toggle: ; switch fields
cmp state,DEST ; are we in destination state?
jne toggle2 ; if not, go to other switch
call CLEAR_FIELD ; clear the field
not state ; switch to message state
mov c_field, OFFSET m_index ; switch to correct field desc.
mov dh,IROW + 3 ; display message text
mov dl,ICOL + 1
call CURSOR
mov di, OFFSET buffer.MB_text
call PRT_STR
mov dh,IROW + 2 ; display message prompt
mov dl,ICOL + 1
CALL CURSOR
mov di,OFFSET prompt2 ; display MESSAGE: prompt
call PRT_STR
mov dh,IROW + 3 ; move cursor to proper spot
mov dl,ICOL + 1
mov si,c_field ; get address of field record
add dl,[si].fd_index
call CURSOR
jmp Break
Toggle2:
call CLEAR_FIELD ; clear the field
not state ; switch to message state
mov c_field, OFFSET d_index ; switch to correct field desc.
mov dh,IROW + 3 ; display destination
mov dl,ICOL + 1
call CURSOR
mov di, OFFSET buffer.MB_machine
call PRT_STR
mov dh,IROW + 2 ; display destination prompt
mov dl,ICOL + 1
call CURSOR
mov di,OFFSET prompt1
call PRT_STR
mov dh,IROW + 3 ; move cursor to proper spot
mov dl,ICOL + 1
mov si,c_field ; get address of field record
add dl,[si].fd_index ; get current cursor position
call CURSOR
jmp Break
N2: cmp ax,LEFT
jne N3
mov si,c_field ; check cursor position
cmp [si].FD_index,0 ; if at left edge, do nothing
jne LCONT
jmp Break
LCONT:
dec [si].FD_index ; move one position left
dec dl ; move cursor
jmp Break
N3: cmp ax,RIGHT
jne N4
mov si,c_field ; check cursor position
mov ah,[si].FD_index
cmp ah,[si].FD_maxlen
jl RCONT
jmp Break
RCONT: ; if at right edge, do nothing
inc [si].FD_index ; move one position right
inc dl ; move cursor
jmp Break
N4: cmp ax,BACK
jne N5
mov si,c_field ; check cursor position
cmp [si].FD_index,0 ; if at left edge, do nothing
jg BCONT
jmp Break
BCONT:
dec [si].FD_index ; move one column left
dec dl ; move cursor too
call CURSOR
xor cx,cx
mov cl,[si].FD_index ; current pointer position
mov si,[si].FD_data ; offset of start of current string
add si,cx ; point to current position
call DELETE_CHAR
jmp Break
N5: cmp ax,RETURN
jne N6
; see if we're ready to send a message
cmp d_index.FD_length,0 ; see if destination is set
jg RET1
jmp Toggle ; if not, just switch fields
RET1:
cmp m_index.FD_length,0 ; see if message is ready
jg RET2 ; if not, just switch fields
jmp Toggle
; if so, compose the message and send it
RET2:
mov ax,5F98H ; send message interrupt
mov si,OFFSET buffer ; message buffer
int 21h ; send message
; clear fields and get ready for next message
call CLEAR_FIELD
mov m_index.FD_length,0 ; zero pointers an length for message
mov m_index.FD_index,0
mov d_index.FD_length,0 ; zero pointers and length for dest
mov d_index.FD_index,0
mov buffer.MB_machine,0 ; zero message and dest buffers
mov buffer.MB_text,0
mov state,MSG
jmp Toggle ; toggle state to DEST again...
jmp Break
N6: cmp ax,INSERT
jne N7
not ins_flag ; toggle insert flag
jmp Break
N7: cmp ax,DELETE
jne N8
mov si,c_field
cmp [si].FD_length,0 ; if length is 0, nothing to delete
jg DELCONT
jmp Break
DELCONT:
xor cx,cx
mov cl,[si].FD_index ; current pointer position
mov si,[si].FD_data ; offset of start of current string
add si,cx ; point to current position
call DELETE_CHAR
jmp Break
N8: cmp ax,HOME
jne N9
xor bx,bx ; get us a zero to work with
mov si,c_field ; get current field address
mov [si].FD_index,bl
mov dl,ICOL + 1 ; reset current cursor position
jmp Break
N9: cmp ax,ESCAPE
jne N10
mov done,1
jmp Break
N10: cmp ax,ENDKEY
jne Default
mov si,c_field
mov bl,[si].FD_length
mov [si].FD_index,bl
mov dl,ICOL + 1
add dl,bl
jmp Break
Default:
mov si,c_field
mov bl,[si].FD_length
mov bh,[si].FD_maxlen
cmp bl,bh ; see if we've hit the end of field
jge Break ; if so, do nothing
cmp state,DEST ; see if we're in destination field
jne DEFCON ; if not, don't shift
call UPCASE
DEFCON:
cmp ins_flag,0 ; see if we're in insert state
je OVERWRITE ; if not, overwrite
call INSERT_CHAR
jmp Break
OVERWRITE:
mov ah,[si].FD_index ; save the character
CALL STORE_CHAR
PUTC al,1 ; write character
inc dl ; increment cursor position
mov cl,[si].FD_index ; get current position
inc cl ; increment char pointer
mov [si].FD_index,cl ; save new pointer
cmp cl,[si].FD_length ; are we appending to the string?
jl Break ; if not, we're done
mov ah,[si].FD_length ; otherwise, get length pointer
inc ah ; increment string length
mov [si].FD_length,ah ; save new length
xor al,al ; save a terminating NULL char
CALL STORE_CHAR
Break:
cmp cs:done,1
je shutdown
jmp get_input
shutdown:
;
; Restore screen data
;
mov si, OFFSET screen_data ; point to save area
mov dh,IROW
mov cx,IHEIGHT ; number of rows to do
doRow1: ; For each row...
push cx ; save number of rows
mov dl,ICOL ; starting column on screen
call CURSOR ; move cursor to row start
mov cx,IWIDTH ; number of columns
DoCol1: ; For each character
push cx ; save char count
lodsw ; get next word from save area
@PutChAtr al,ah,0,1 ; write next picture char
inc dl ; move cursor
call CURSOR
pop cx ; retrieve char count
loop SHORT DoCol1 ; Go back for next char
inc dh ; point to next row
pop cx ; retrieve row count
loop SHORT DoRow1 ; Go back for next char
;
; Restore previous LANOS message handler.
;
; Set MSR vector to original routine
mov ax,word ptr old_msr+2 ; segment addr of old routine
mov es,ax
mov bx,word ptr old_msr ; offset of old routine
mov ax,5FE3h ; set MSR vector fn.
int 21H
;
; Restore the message processing flag
;
mov ax,5F9Bh ; set message processing flag
mov dl,cs:msg_state ; back to it's previous state
int 21h
; restore cursor to original position
mov ah,2 ; set cursor position
mov bh,0 ; for page 0
mov dx,cs:old_cursor
int 10h
bypass:
@OldStack ; restore caller's stack
ret ; return to caller
pop_up ENDP
;*********************************************************************
; Subroutines
;********************************************************************
; Move cursor -- row in dh, col in dl
CURSOR PROC NEAR
push ax
push bx
push cx
@SetCurPos dl,dh,0
pop cx
pop bx
pop ax
ret
CURSOR ENDP
PRT_STR PROC NEAR
;
; Write ASCIIZ string pointed to by ES:DI at current cursor position
;
L3:
mov al,es:[di] ; get the character
or al,al ; See if it's a zero
jz L4 ; Yes- we're done
mov bx,7 ; display page 0, fg color 7
mov ah,0Eh ; Write char / tty mode
int 10h ; Display the character
inc di ; get the next char
jmp short L3
L4:
ret
PRT_STR ENDP
CLEAR_FIELD PROC NEAR
;
; clear the input field
;
mov dh,IROW + 3
mov dl,ICOL + 1
call CURSOR
mov si,c_field
xor cx,cx
mov cl,[si].FD_maxlen
PUTC ' ',cx
mov dh,IROW + 3
mov dl,ICOL + 1
call CURSOR
ret
CLEAR_FIELD ENDP
DELETE_CHAR PROC NEAR
;
; Gets rid of a the character in the asciiz string currently
; pointed to by DS:SI. Assumes that ES is also pointing to
; the string's segment. Handles all screen IO, assuming the
; cursor was positioned at the character to be deleted.
;
push dx ; save starting cursor position
mov di,si ; save offset of char to go
push di ; save offset for printing later
inc si ; skip the current char
DL1:
lodsb ; get the next char
stosb ; save it in the previous slot
cmp al,0 ; is a terminator?
jne DL1 ; if not, go back for the next char
pop di ; get the old string location back
call PRT_STR ; print the modified string
PUTC ' ',1
pop dx ; restore starting cursor position
mov si,c_field ; update housekeeping info
dec [si].FD_length
ret
DELETE_CHAR ENDP
STORE_CHAR PROC NEAR
;
; Store a character in the current field string. Takes the character
; in Al and the position in the string in AH. Uses the BX register
;
push bx ; save the registers used
push cx
xor cx,cx ; clear CX ...
mov cl,ah ; load w/ offset value
mov bx,c_field
mov bx,[bx].FD_data ; offset of string buffer
add bx,cx ; + position in buffer
mov [bx],al ; save character
pop cx ; restore registers
pop bx
ret
STORE_CHAR ENDP
UPCASE PROC NEAR
;
; If the character in al is a lower case letter, convert to uppercase
;
cmp al,97 ; compare to 'a'
jl SHL1 ; if lower, return
cmp al,122 ; compare to 'z'
jg SHL1 ; if higher, return
sub al,32 ; convert to uppercase
SHL1:
ret
UPCASE ENDP
INSERT_CHAR PROC NEAR
;
; Inserts the character currently in AL in the current string at the
; location indicated by AH. Assumes that ES is also pointing to the string's
; segment and that the cursor is located at the right spot.
;
push dx ; save starting cursor position
xor cx,cx ; point SI to correct location
mov si,c_field
mov cl,[si].FD_index
mov si,[si].FD_data
add si,cx
mov di,si ; copy string pointer to DI
IL1:
mov ah,[si] ; old character in ah
mov [si],al ; new char in al
mov al,ah ; move old to new
inc si ; move to next char position
and ah,ah ; see if it's a zero
jnz IL1 ; if not, go back for next one
; display the string
CALL PRT_STR
; Update housekeeping information
mov si,c_field
inc [si].FD_index
inc [si].FD_length
pop dx ; restore cursor location
inc dl
ret
INSERT_CHAR ENDP
SHOW_MESSAGE PROC NEAR
;
; display message in message area
;
push dx ; save current cursor loc
mov ax,5F99h ; get last message fn.
mov di,OFFSET inbuffer ; point to receive area
int 21h ; get the message
;
; Display last received message and previous contents of message buffer
;
mov dh,IROW ; display originator's name
mov dl,ICOL + 7
call CURSOR
PUTC '─',16 ; clear field
call CURSOR
; get rid of trailing spaces
mov di, OFFSET inbuffer.MB_originator
mov al,32 ; scan for spaces
mov cx,16 ; max field length
repnz scasb
mov [di],ch ; terminate string
; print the string
mov di, OFFSET inbuffer.MB_originator
call PRT_STR
; display message text
; -- first make silly sound
mov al,0B6h ; timer setup value
out 43h,al ; tell timer a count's coming
mov ax,397 ; frequency -- about 3000 hertz
out 42h,al ; send the count
mov al,ah
out 42h,al
xor ah,ah
in al,SOUND ; turn on speaker
or al,SPK_ON
out SOUND,al
mov dh,IROW + 1 ; display message text
mov dl,ICOL + 1
call CURSOR
PUTC ' ',61
call CURSOR
mov di, OFFSET inbuffer.MB_text
call PRT_STR
; turn off speaker
in al,sound
and al, NOT SPK_ON
out sound,al
pop dx ; restore cursor position
mov msg_flag,0 ; reset received flag
ret
SHOW_MESSAGE ENDP
; write character in al at current cursor position
; char count should be in CX
PUTCHAR PROC NEAR
mov bl,color
xor bh,bh
mov ah,09h
int 10h
ret
PUTCHAR ENDP
;
; Draw one line of INTERCOM's screen window
;
LINE PROC NEAR
push bx
push bx
push ax
push bx
MOVE_CURSOR bh,ICOL ; move to beginning of line
PUTC al,1 ; display leading character
pop bx ; retrieve row
MOVE_CURSOR bh,ICOL+1 ; move to 2nd char of line
pop ax ; retrieve char to display
mov al,ah ; move char to al
PUTC al,IWIDTH-2 ; display line 'middle' character
pop bx ; retrieve row coords
MOVE_CURSOR bh,ICOL+IWIDTH-1; move to last char of line
pop ax ; retrieve character
PUTC al,1 ; display line 'end' character
ret
LINE ENDP
LAST_BYTE EQU $
;
; Initialization -- thrown away after load
;
title1 db " SoftMagic Resident Intercom V1.0 for LANtastic",13,10,0
title2 db "Copyright 1989 by SoftMagic, Inc. All rights Reserved.",13,10,0
title3 db " LANtastic is a trademark of Artisoft, Inc.",13,10,0
title4 db 13,10,"The SoftMagic Resident Intercom is already installed.",13,10,0
IFDEF SHAREWARE
title5 db 7,"Thanks for trying this unregistered ShareWare Version!",7,7,13,10,0
ENDIF
init: mov cs:busy,1 ; prevent activation
@NewStack ; Set up local stack
; Display title messge
mov di,OFFSET title1
call PRT_STR
mov di,OFFSET title2
call PRT_STR
mov di,OFFSET title3
call PRT_STR
IFDEF SHAREWARE
mov di,OFFSET title5
call PRT_STR
ENDIF
;
; Get the location of the DOS Busy flag
;
; mov ax,3400h ; Flag is zero if DOS is safe
; int 21h
; mov dos_busy_seg,es
; mov dos_busy_offs,bx
;
; Replace the keyboard interrupt handler
;
mov ah,35h ; Get int vector fcn.
mov al,VECTOR1 ; vector to get
int 21h ; Get the silly thing
; Save the old vector
mov word ptr old_vector1,bx ; Save location of old vector
mov word ptr old_vector1+2,es
;
; Set Interrupt vector to our routine
;
mov ah,25h ; Set vector fcn
mov al,VECTOR1 ; Vector to replace
mov dx,OFFSET INT09_entry ; Pointer to our routine
int 21h ; Replace the vector
;
; Replace LANtastic's internal DOS free vector
;
MOV AX,5FE0H
INT 21H ; GET THE DOS FREE VECTOR
MOV WORD PTR old_vector2,BX
MOV WORD PTR old_vector2+2,ES
;
; Set vector to our routine
;
push cs
pop es
MOV BX,OFFSET DOSFREE
MOV AX,5FE1H
INT 21H
; Terminate and stay resident
@OldStack ; Restore caller's stack
mov cs:busy, 0 ; Enable pop-up
mov dx,(offset LAST_BYTE - SEG_ORG + 15) shr 4
mov ah,31h ; free memory and leave
int 21h
main ENDP
msv ENDS
END start